﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using NhibernateClassBuilder.Properties;

namespace CreaMotion
{
   public class NhibernateClassGenerator
   {
        #region Database Source

       public string DatabaseName;
       public List<Interfaces.Table> TableSource;
       public List<Interfaces.Relationship> RelationshipSource;

       #endregion

        #region NHibernateSessionManager

       Dictionary<ProgrammingLanguage, String> SessionManager
                                = new Dictionary<ProgrammingLanguage, string>()
                                {
                                    {
                                        ProgrammingLanguage.Vb,
                                        Resources.vbSessionManager
                                    },
                                    {
                                        ProgrammingLanguage.Csharp,
                                        Resources.csSessionManager
                                    }
                                };
       #endregion

        #region Schemas

        #region Hbm Schemas

        string HbmClassSchema ="<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
                                "\n<hibernate-mapping xmlns=\"urn:nhibernate-mapping-2.2\">" +
                                "\n<class name=\"{0}.{1},{0}\" table=\"{1}\">" +
                                "\n{2}" +
                                "\n</class>" +
                                "\n</hibernate-mapping>";

        string HbmBagSchema =  "\n    <bag name=\"{0}\" lazy=\"true\" cascade=\"save-update\" inverse=\"true\" >" +
                               "\n      <key column=\"{1}\" />" +
                               "\n      <{4}-to-many class=\"{2}.{3},{2}\"  />" +
                               "\n    </bag>\n";

        string HbmPKSchema = "    <id name=\"{0}\" type=\"{1}\">" +
                             "\n      <generator class=\"identity\" />" +
                             "\n    </id>\n";

        string HbmPropSchema = "    <property name=\"{0}\" column=\"{0}\" type=\"{1}\" /> \n";

        string HbmRelationshipSchema = "    <{0}-to-one name=\"{1}\" class=\"{2}.{3},{2}\" /> \n";

       #endregion

        #region Code Schemas

        #region NamespaceCodes

        Dictionary<ProgrammingLanguage, String> NamespaceCodes =
         new Dictionary<ProgrammingLanguage, string>(2) {
                                                            {
                                                                ProgrammingLanguage.Vb,
                                                                "Namespace {0} \n{1}\nEnd NameSpace"
                                                            },
                                                            {
                                                                ProgrammingLanguage.Csharp,
                                                                "using System;\nusing System.Collections.Generic;\nnamespace {0}\n{{\n{1}\n}}"
                                                            }
                                                        };

        #endregion

        #region ClassCodes

        Dictionary<ProgrammingLanguage, String> ClassCodes =
         new Dictionary<ProgrammingLanguage, string>(2) {
                                                            {
                                                                ProgrammingLanguage.Vb,
                                                                "Public Class {0}\n{1}\n     End Class"
                                                            },
                                                            {
                                                                ProgrammingLanguage.Csharp,
                                                                "public class {0} \n     {{\n{1}     \n     }}"
                                                            }
                                                        };

        #endregion

        #region PropertyCodes

        Dictionary<ProgrammingLanguage, String> PropertyCodes =
                new Dictionary<ProgrammingLanguage, string>(2) {
                                                                    {
                                                                        ProgrammingLanguage.Vb,
                                                                        new string(" "[0],10) + "Public Overridable Property {0} as {1} \n"
                                                                    },
                                                                    {
                                                                        ProgrammingLanguage.Csharp,
                                                                        new string(" "[0],10) + "public virtual {1} {0} {{get;set;}} \n"
                                                                    }
                                                                };

        #endregion

        #region IListSchema

        Dictionary<ProgrammingLanguage, string> IListSchema
                        = new Dictionary<ProgrammingLanguage, string>()
                                {
                                    {
                                        ProgrammingLanguage.Vb,
                                        Resources.vbIListSchema
                                    },
                                    {
                                        ProgrammingLanguage.Csharp,
                                        Resources.csIListSchema
                                    }
                                };


        #endregion

        #region AddPropertyCode
        Dictionary<ProgrammingLanguage,String> AddPropertyCode =
           new Dictionary<ProgrammingLanguage,string> () {
                                                            {
                                                                ProgrammingLanguage.Vb,
                                                                Resources.vbAddPropertyCode
                                                            },
                                                            {
                                                                ProgrammingLanguage.Csharp,
                                                                Resources.csharpAddPropertyCode
                                                            }
                                                         };

        #endregion

        #region Extensions

        Dictionary<ProgrammingLanguage, String> Extensions =
                  new Dictionary<ProgrammingLanguage, string>() {
                                                                  {ProgrammingLanguage.Vb,".vb"},
                                                                  {ProgrammingLanguage.Csharp,".cs"}
                                                               };

        #endregion
       
       #endregion
        #endregion

        List<String> copyList = new List<string>()
                                                    {
                                                        ".\\Iesi.Collections.dll",
                                                        ".\\NHibernate.dll",
                                                    };

        #region ProgrammingLanguage

        public enum ProgrammingLanguage
       {
           Vb,
           Csharp,
           Java
       }

       #endregion

        #region Generator Project Sub

       public void GeneratorProject(String FolderPath,String Namespace,ProgrammingLanguage PL)
       {
           string Code = string.Empty;

           string ClassSchema = string.Empty;
           string ClassesCode = string.Empty;

           foreach (Interfaces.Table table in TableSource)
           {
               string propertySchema = string.Empty;
               string propertyCode = string.Empty;
               
               foreach (Interfaces.Field field in table.Fields)
               {
                   //Property Code Generate
                   propertyCode += GeneratePropertyCode(PL,field);

                   //Property Schema Generate
                   propertySchema += GeneratePropertySchema(field);
                   
                   //Find Relationships And Check It.
                   Interfaces.Relationship[] Relationships = FindRelationships(field, table);
                   if(Relationships == null) continue;
                   
                   //Relationship Generic Array Code Generate
                   propertyCode += GenerateRelationshipCode(PL,Relationships);

                   //Relationship Generate
                   propertySchema += GenerateRelationshipSchema(Namespace,Relationships);
               }
               
               //Class Code Generate
               ClassesCode += new string(" "[0], 5) +
                     String.Format(ClassCodes[PL], table.TableName, propertyCode) + "\n";

               //Fill Class Schema And Save It
               ClassSchema = String.Format(HbmClassSchema, Namespace, table.TableName, propertySchema);
               File.WriteAllText(Path.Combine(FolderPath, String.Concat(table.TableName, ".hbm.xml")), ClassSchema);
           }

           //Add NameSpace Code
           Code = String.Format(NamespaceCodes[PL], Namespace, ClassesCode);

           //Save Classes
           File.WriteAllText(Path.Combine(FolderPath, String.Concat(DatabaseName, Extensions[PL])), Code);
           

           string props = string.Empty;
           foreach (var item in NHibernateSessionManager.NHibernatenProperties)
           {
               props += String.Format(AddPropertyCode[PL], item.Key, item.Value);
           }

           //Save Session Manager
           File.WriteAllText(Path.Combine(FolderPath, String.Concat("NHibernateSessionManager", Extensions[PL])), String.Format(SessionManager[PL], Namespace, props));

           Directory.CreateDirectory(Path.Combine(FolderPath, "References"));
           foreach (string CopyItem in copyList)
           {
               File.Copy(CopyItem, Path.Combine(FolderPath, "References", Path.GetFileName(CopyItem)));
           }
       }

        #endregion

        #region Relationship

        private Interfaces.Relationship[] FindRelationships(Interfaces.Field field,Interfaces.Table table)
        {
            return (from RS in RelationshipSource
                    where RS.FirstField == field && RS.FirstTableName == table.TableName
                    select RS).ToArray();
        }

        private String GenerateRelationshipSchema(String Namespace ,params Interfaces.Relationship[] relationships)
        {
           string ReturnData = string.Empty;

           foreach (Interfaces.Relationship relationship in relationships)
           {
               if (relationship.SecoundField.RelationshipType == Interfaces.Field.RType.many)
               {
                   ReturnData += String.Format(HbmBagSchema,
                                               relationship.SecoundTableName + "s",
                                               relationship.SecoundField.FieldName,
                                               Namespace,
                                               relationship.SecoundTableName,
                                               relationship.FirstField.RelationshipType.ToString("F"));
               }
               else
               {
                   ReturnData += String.Format(HbmRelationshipSchema,
                                               relationship.FirstField.RelationshipType,
                                               "_" + relationship.SecoundTableName,
                                               Namespace,
                                               relationship.SecoundTableName);
               }
           }

           return ReturnData;
        }


        private string GenerateRelationshipCode(ProgrammingLanguage language, params Interfaces.Relationship[] relationships)
        {
            string ReturnData = string.Empty;

            foreach (Interfaces.Relationship relationship in relationships)
            {

                if (relationship.SecoundField.RelationshipType == Interfaces.Field.RType.many)
                {

                    ReturnData += String.Format(IListSchema[language],
                                                relationship.SecoundTableName,
                                                relationship.SecoundTableName + "s");

                }
                else
                {
                    ReturnData += String.Format(PropertyCodes[language],
                                                "_" + relationship.SecoundTableName,
                                                relationship.SecoundTableName);
                }
            }
            return ReturnData;
        }

       #endregion

        #region Property

        private string GeneratePropertyCode(ProgrammingLanguage language,Interfaces.Field field)
         {
           return String.Format(PropertyCodes[language], field.FieldName, field.FieldType_net.Name);
         }

        private string GeneratePropertySchema(Interfaces.Field field)
        {
           return String.Format(field.IsPrimaryKey == true ? HbmPKSchema : HbmPropSchema,
                                field.FieldName, field.FieldType_net.Name);
        }

        #endregion
   }
}
